package com.template.utils;

import cn.hutool.jwt.JWT;
import com.template.common.exception.IdentityException;
import com.template.common.lang.Const;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtUtils {

    private static final String KEY = "as1dfSDO2fgnSDN03dDNi4nfgDLK3JSD9nfg"; // 密文
    private static final long EXPIRE_SECONDS = 3600 * 24 * 3; // 3天
    private static final long EXPIRE_MILLISECONDS = EXPIRE_SECONDS * 1000;

    @Resource
    RedisUtils redisUtils;

    /**
     * 生成token
     */
    public static String createToken(Long userId) {
        Date date = new Date(System.currentTimeMillis() + EXPIRE_MILLISECONDS);

        // 默认使用HS265算法
        return JWT.create()
                .setPayload(KEY, userId)
                .setExpiresAt(date)
                .setKey(KEY.getBytes())
                .sign();
    }

    /**
     * 验证token正确性和时效性
     */
    public static boolean validate(String jwtToken) {
        if (jwtToken == null || jwtToken.trim().equals("")) return false;
        return JWT.of(jwtToken).setKey(KEY.getBytes()).validate(0);
    }

    /**
     * 获取数据
     */
    public static Long getVal(String jwtToken) {
        if (jwtToken == null || jwtToken.trim().equals("")) return null;

        try {
            String userId = JWT.of(jwtToken).getPayload(KEY).toString();
            return Long.parseLong(userId);
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * 创建并缓存token
     */
    public String createAndCacheToken(Long userId) {
        String token = createToken(userId);
        boolean cacheFlag = cacheToken(userId, token);
        if (!cacheFlag) throw new IdentityException("缓存token失败");
        return token;
    }

    /**
     * 缓存token
     */
    public boolean cacheToken(Long userId, String token) {
        return redisUtils.set(getKey(userId), token, EXPIRE_SECONDS);
    }

    /**
     * 获取缓存的token
     */
    public String getCache(Long userId) {
        return (String) redisUtils.get(getKey(userId));
    }

    /**
     * 获取临时缓存
     */
    public String getCacheTmp(Long userId) {
        return (String) redisUtils.get(Const.cacheTmpToken(userId));
    }

    /**
     * 刷新token
     * <p>
     * 利用双token校验机制，避免刷新token与其他请求同时进行，导致其他请求中的token失效
     * <p>
     * 将 旧token有效期 设置极短，达到延迟删除的效果
     * 再生成 新token
     */
    public String refreshToken(Long userId, String oldToken) {
        // 验证token
        // （拦截器已验证）

        // 暂存旧token，延迟 5s 删除
        removeToken(userId);
        redisUtils.set(Const.cacheTmpToken(userId), oldToken, 5);

        // 生成新token
        String newToken = createToken(userId);

        // 保存新token
        boolean cacheFlag = cacheToken(userId, newToken);
        if (!cacheFlag) throw new IdentityException("Token刷新失败");

        return newToken;
    }

    /**
     * 双token验证
     */
    public boolean validWithCache(String token) {
        boolean validToken = JwtUtils.validate(token);
        if (!validToken) return false;
        Long userId = JwtUtils.getVal(token);
        if (userId == null) return false;

        return validWithCache(token, userId);
    }

    public boolean validWithCache(String token, Long userId) {
        // 获取缓存token
        String cacheToken = getCache(userId);

        if (StringUtils.equals(token, cacheToken)) return true;

        // 用户的token与缓存的token不一致，可能存在临时token
        String cacheTmpToken = getCacheTmp(userId);
        return StringUtils.equals(token, cacheTmpToken);
    }

    /**
     * 删除token
     */
    public void removeToken(Long userId) {
        redisUtils.del(getKey(userId));
    }

    private String getKey(Long userId) {
        return  Const.Cache.TOKEN + ":" + userId;
    }


}
